home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / gdiff-2.2 / sdiff.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  22KB  |  1,068 lines

  1. /* SDIFF -- interactive merge front end to diff
  2.    Copyright (C) 1992 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU SDIFF was written by Thomas Lord. */
  21.  
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include "system.h"
  25. #include <signal.h>
  26. #include "getopt.h"
  27.  
  28. #ifndef SEEK_SET
  29. #define SEEK_SET 0
  30. #endif
  31.  
  32. /* Size of chunks read from files which must be parsed into lines. */
  33. #define SDIFF_BUFSIZE 65536
  34.  
  35. /* Default name of the diff program */
  36. #ifndef DIFF_PROGRAM
  37. #define DIFF_PROGRAM "/usr/bin/diff"
  38. #endif
  39.  
  40. /* Users' editor of nonchoice */
  41. #ifndef DEFAULT_EDITOR
  42. #define DEFAULT_EDITOR "ed"
  43. #endif
  44.  
  45. extern char *version_string;
  46. static char const *prog;
  47. static char const *diffbin = DIFF_PROGRAM;
  48. static char const *edbin = DEFAULT_EDITOR;
  49.  
  50. static char *tmpname;
  51. static int volatile tmpmade;
  52. static pid_t volatile diffpid;
  53.  
  54. struct line_filter;
  55. static void diffarg (); /* (char *); */
  56. static void execdiff (); /* (int, char const *, char const *, char const *); */
  57. static int edit (); /* (struct line_filter *left, int lenl, struct
  58.                line_filter *right, int lenr, FILE *outfile); */
  59. static int interact (); /* (struct line_filter *diff,
  60.               struct line_filter *left,
  61.               struct line_filter *right, FILE *outfile); */
  62. static void trapsigs (); /* (void); */
  63. /* this lossage until the gnu libc conquers the universe */
  64. #define TMPNAMSIZE 1024
  65. #define PVT_tmpdir "/tmp"
  66. static char *private_tempnam (); /* (const char *, const char *, int, int *); */
  67. static int diraccess ();
  68.  
  69. /* Options: */
  70.  
  71. /* name of output file if -o spec'd */
  72. static char *out_file;
  73.  
  74. /* do not print common lines if true, set by -s option */
  75. static int suppress_common_flag;
  76.  
  77. static struct option longopts[] =
  78. {
  79.   {"ignore-blank-lines", 0, NULL, 'B'},
  80.   {"speed-large-files", 0, NULL, 'H'},
  81.   {"ignore-matching-lines", 1, NULL, 'I'},
  82.   {"ignore-all-space", 0, NULL, 'W'}, /* swap W and w for historical reasons */
  83.   {"text", 0, NULL, 'a'},
  84.   {"ignore-space-change", 0, NULL, 'b'},
  85.   {"minimal", 0, NULL, 'd'},
  86.   {"ignore-case", 0, NULL, 'i'},
  87.   {"left-column", 0, NULL, 'l'},
  88.   {"output", 1, NULL, 'o'},
  89.   {"suppress-common-lines", 0, NULL, 's'},
  90.   {"expand-tabs", 0, NULL, 't'},
  91.   {"width", 1, NULL, 'w'},
  92.   {"version", 0, NULL, 'v'},
  93.   {NULL, 0, NULL, 0}
  94. };
  95.  
  96. /* prints usage message and quits */
  97. static void
  98. usage ()
  99. {
  100.   fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog);
  101.   fprintf (stderr, "Options:\n\
  102.        [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
  103.        [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\
  104.        [--ignore-case] [--ignore-matching-lines=regexp]\n\
  105.        [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\
  106.        [--suppress-common-lines] [--left-column] [--output=outfile]\n\
  107.        [--version] [--width=columns]\n");
  108.   exit (2);
  109. }
  110.  
  111. static void
  112. cleanup ()
  113. {
  114.   if (0 < diffpid)
  115.     kill (diffpid, SIGPIPE);
  116.   if (tmpmade)
  117.     unlink (tmpname);
  118. }
  119.  
  120. static void
  121. exiterr ()
  122. {
  123.   cleanup ();
  124.   exit (2);
  125. }
  126.  
  127. static void
  128. fatal (msg)
  129.      char *msg;
  130. {
  131.   fprintf (stderr, "%s: %s\n", prog, msg);
  132.   exiterr ();
  133. }
  134.  
  135. static void
  136. perror_fatal (msg)
  137.      char *msg;
  138. {
  139.   int e = errno;
  140.   fprintf (stderr, "%s: ", prog);
  141.   errno = e;
  142.   perror (msg);
  143.   exiterr ();
  144. }
  145.  
  146.  
  147. /* malloc freely or DIE! */
  148. char *
  149. xmalloc (size)
  150.      size_t size;
  151. {
  152.   char *r = malloc (size);
  153.   if (!r)
  154.     fatal ("virtual memory exhausted");
  155.   return r;
  156. }
  157.  
  158. static FILE *
  159. ck_fopen (fname, type)
  160.      char *fname, *type;
  161. {
  162.   FILE *r = fopen (fname, type);
  163.   if (!r)
  164.     perror_fatal (fname);
  165.   return r;
  166. }
  167.  
  168.  
  169. static FILE *
  170. ck_fdopen (fd, type)
  171.      int fd;
  172.      char *type;
  173. {
  174.   FILE *r = fdopen (fd, type);
  175.   if (!r)
  176.     perror_fatal ("fdopen");
  177.   return r;
  178. }
  179.  
  180. static void
  181. ck_fclose (f)
  182.      FILE *f;
  183. {
  184.   if (fclose (f))
  185.     perror_fatal ("input/output error");
  186. }
  187.  
  188. static size_t
  189. ck_fread (buf, size, f)
  190.      char *buf;
  191.      size_t size;
  192.      FILE *f;
  193. {
  194.   size_t r = fread (buf, sizeof (char), size, f);
  195.   if (r == 0 && ferror (f))
  196.     perror_fatal ("input error");
  197.   return r;
  198. }
  199.  
  200. static void
  201. ck_fwrite (buf, size, f)
  202.      char *buf;
  203.      size_t size;
  204.      FILE *f;
  205. {
  206.   if (fwrite (buf, sizeof (char), size, f) != size)
  207.     perror_fatal ("output error");
  208. }
  209.  
  210. static void
  211. ck_fflush (f)
  212.      FILE *f;
  213. {
  214.   if (fflush (f) != 0)
  215.     perror_fatal ("output error");
  216. }
  217.  
  218. #if !HAVE_MEMCHR
  219. char *
  220. memchr (s, c, n)
  221.      char *s;
  222.      int c;
  223.      size_t n;
  224. {
  225.   unsigned char *p = (unsigned char *) s, *lim = p + n;
  226.   for (;  p < lim;  p++)
  227.     if (*p == c)
  228.       return (char *) p;
  229.   return 0;
  230. }
  231. #endif
  232.  
  233. #ifndef HAVE_WAITPID
  234. /* Emulate waitpid well enough for sdiff, which has at most two children.  */
  235. static pid_t
  236. waitpid (pid, stat_loc, options)
  237.      pid_t pid;
  238.      int *stat_loc;
  239.      int options;
  240. {
  241.   static int ostatus;
  242.   static pid_t opid;
  243.   int npid, status;
  244.  
  245.   if (pid == opid)
  246.     {
  247.       opid = 0;
  248.       status = ostatus;
  249.     }
  250.   else
  251.     while ((npid = wait (&status)) != pid)
  252.       {
  253.     if (npid < 0)
  254.       return npid;
  255.     opid = npid;
  256.     ostatus = status;
  257.       }
  258.   *stat_loc = status;
  259.   return pid;
  260. }
  261. #endif
  262.  
  263. static char const *
  264. expand_name (name, isdir, other_name)
  265.      char *name;
  266.      int isdir;
  267.      char const *other_name;
  268. {
  269.   if (strcmp (name, "-") == 0)
  270.     fatal ("cannot interactively merge standard input");
  271.   if (!isdir)
  272.     return name;
  273.   else
  274.     {
  275.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  276.       const char
  277.     *p = rindex (other_name, '/'),
  278.     *base = p ? p+1 : other_name;
  279.       size_t namelen = strlen (name), baselen = strlen (base);
  280.       char *r = xmalloc (namelen + baselen + 2);
  281.       bcopy (name, r, namelen);
  282.       r[namelen] = '/';
  283.       bcopy (base, r + namelen + 1, baselen + 1);
  284.       return r;
  285.     }
  286. }
  287.  
  288.  
  289.  
  290. struct line_filter {
  291.   FILE *infile;
  292.   char *bufpos;
  293.   char *buffer;
  294.   char *buflim;
  295. };
  296.  
  297. static void
  298. lf_init (lf, infile)
  299.      struct line_filter *lf;
  300.      FILE *infile;
  301. {
  302.   lf->infile = infile;
  303.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  304.   lf->buflim[0] = '\n';
  305. }
  306.  
  307. /* Fill an exhausted line_filter buffer from its INFILE */
  308. static size_t
  309. lf_refill (lf)
  310.      struct line_filter *lf;
  311. {
  312.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  313.   lf->bufpos = lf->buffer;
  314.   lf->buflim = lf->buffer + s;
  315.   lf->buflim[0] = '\n';
  316.   return s;
  317. }
  318.  
  319. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  320. static void
  321. lf_copy (lf, lines, outfile)
  322.      struct line_filter *lf;
  323.      int lines;
  324.      FILE *outfile;
  325. {
  326.   char *start = lf->bufpos;
  327.  
  328.   while (lines)
  329.     {
  330.       lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  331.       if (! lf->bufpos)
  332.     {
  333.       ck_fwrite (start, lf->buflim - start, outfile);
  334.       if (! lf_refill (lf))
  335.         return;
  336.       start = lf->bufpos;
  337.     }
  338.       else
  339.     {
  340.       --lines;
  341.       ++lf->bufpos;
  342.     }
  343.     }
  344.  
  345.   ck_fwrite (start, lf->bufpos - start, outfile);
  346. }
  347.  
  348. /* Advance LINES on LF's infile without doing output */
  349. static void
  350. lf_skip (lf, lines)
  351.      struct line_filter *lf;
  352.      int lines;
  353. {
  354.   while (lines)
  355.     {
  356.       lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  357.       if (! lf->bufpos)
  358.     {
  359.       if (! lf_refill (lf))
  360.         break;
  361.     }
  362.       else
  363.     {
  364.       --lines;
  365.       ++lf->bufpos;
  366.     }
  367.     }
  368. }
  369.  
  370. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  371. static int
  372. lf_snarf (lf, buffer, bufsize)
  373.      struct line_filter *lf;
  374.      char *buffer;
  375.      size_t bufsize;
  376. {
  377.   char *start = lf->bufpos;
  378.  
  379.   for (;;)
  380.     {
  381.       char *next = memchr (start, '\n', lf->buflim + 1 - start);
  382.       size_t s = next - start;
  383.       if (bufsize <= s)
  384.     return 0;
  385.       bcopy (start, buffer, s);
  386.       if (next < lf->buflim)
  387.     {
  388.       buffer[s] = 0;
  389.       lf->bufpos = next + 1;
  390.       return 1;
  391.     }
  392.       if (! lf_refill (lf))
  393.     return s ? 0 : EOF;
  394.       buffer += s;
  395.       bufsize -= s;
  396.       start = next;
  397.     }
  398. }
  399.  
  400.  
  401.  
  402. int
  403. main (argc, argv)
  404.      int argc;
  405.      char *argv[];
  406. {
  407.   int opt;
  408.   int version_requested = 0;
  409.   char *editor = getenv ("EDITOR");
  410.   char *differ = getenv ("DIFF");
  411.  
  412.   prog = argv[0];
  413.   if (editor)
  414.     edbin = editor;
  415.   if (differ)
  416.     diffbin = differ;
  417.  
  418.   diffarg ("diff");
  419.  
  420.   /* parse command line args */
  421.   while ((opt=getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, (int *)0)) != EOF)
  422.     {
  423.       switch (opt)
  424.     {
  425.     case 'a':
  426.       diffarg ("-a");
  427.       break;
  428.  
  429.     case 'b':
  430.       diffarg ("-b");
  431.       break;
  432.  
  433.     case 'B':
  434.       diffarg ("-B");
  435.       break;
  436.  
  437.     case 'd':
  438.       diffarg ("-d");
  439.       break;
  440.  
  441.     case 'H':
  442.       diffarg ("-H");
  443.       break;
  444.  
  445.     case 'i':
  446.       diffarg ("-i");
  447.       break;
  448.  
  449.     case 'I':
  450.       diffarg ("-I");
  451.       diffarg (optarg);
  452.       break;
  453.  
  454.     case 'l':
  455.       diffarg ("--left-column");
  456.       break;
  457.  
  458.     case 'o':
  459.       out_file = optarg;
  460.       break;
  461.  
  462.     case 's':
  463.       suppress_common_flag = 1;
  464.       break;
  465.  
  466.     case 't':
  467.       diffarg ("-t");
  468.       break;
  469.  
  470.     case 'v':
  471.       version_requested = 1;
  472.       fprintf (stderr, "GNU sdiff version %s\n", version_string);
  473.       ck_fflush (stderr);
  474.       break;
  475.  
  476.     case 'w':
  477.       diffarg ("-W");
  478.       diffarg (optarg);
  479.       break;
  480.  
  481.     case 'W':
  482.       diffarg ("-w");
  483.       break;
  484.  
  485.     default:
  486.       usage ();
  487.     }
  488.     }
  489.  
  490.   /* check: did user just want version message? if so exit. */
  491.   if (version_requested && argc - optind == 0)
  492.     exit (0);
  493.  
  494.   if (argc - optind != 2)
  495.     usage ();
  496.  
  497.   if (! out_file)
  498.     /* easy case: diff does everything for us */
  499.     execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]);
  500.   else
  501.     {
  502.       FILE *left, *right, *out, *diffout;
  503.       int diff_fds[2];
  504.       int interact_ok;
  505.       pid_t pid;
  506.       struct line_filter lfilt;
  507.       struct line_filter rfilt;
  508.       struct line_filter diff_filt;
  509.       int leftdir = diraccess (argv[optind]);
  510.       int rightdir = diraccess (argv[optind + 1]);
  511.  
  512.       if (leftdir && rightdir)
  513.     fatal ("both files to be compared are directories");
  514.  
  515.       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
  516.       ;
  517.       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
  518.       out = ck_fopen (out_file, "w");
  519.  
  520.       if (pipe (diff_fds))
  521.     perror_fatal ("pipe");
  522.  
  523.       trapsigs ();
  524.  
  525.       diffpid = pid = vfork ();
  526.  
  527.       if (pid == 0)
  528.     {
  529.       signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
  530.       signal (SIGPIPE, SIG_DFL);
  531.  
  532.       close (diff_fds[0]);
  533.       if (diff_fds[1] != fileno (stdout))
  534.         {
  535.           dup2 (diff_fds[1], fileno (stdout));
  536.           close (diff_fds[1]);
  537.         }
  538.  
  539.       execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]);
  540.     }
  541.  
  542.       if (pid < 0)
  543.     perror_fatal ("fork failed");
  544.  
  545.       close (diff_fds[1]);
  546.       diffout = ck_fdopen (diff_fds[0], "r");
  547.  
  548.       lf_init (&diff_filt, diffout);
  549.       lf_init (&lfilt, left);
  550.       lf_init (&rfilt, right);
  551.  
  552.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  553.  
  554.       ck_fclose (diffout);
  555.       ck_fclose (left);
  556.       ck_fclose (right);
  557.       ck_fclose (out);
  558.  
  559.       {
  560.     int wstatus;
  561.  
  562.     if (waitpid (pid, &wstatus, 0) < 0)
  563.       perror_fatal ("wait failed");
  564.     diffpid = 0;
  565.  
  566.     if (tmpmade)
  567.       {
  568.         unlink (tmpname);
  569.         tmpmade = 0;
  570.       }
  571.  
  572.     if (! interact_ok)
  573.       exit (2);
  574.  
  575.     if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
  576.       fatal ("Subsidiary diff failed");
  577.  
  578.     exit (WEXITSTATUS (wstatus));
  579.       }
  580.     }
  581.   return 0;            /* Fool -Wall . . . */
  582. }
  583.  
  584. static char **diffargv;
  585.  
  586. static void
  587. diffarg (a)
  588.      char *a;
  589. {
  590.   static unsigned diffargs, diffargsmax;
  591.  
  592.   if (diffargs == diffargsmax)
  593.     {
  594.       if (! diffargsmax)
  595.     {
  596.       diffargv = (char **) xmalloc (sizeof (char));
  597.       diffargsmax = 8;
  598.     }
  599.       diffargsmax *= 2;
  600.       diffargv = (char **) realloc (diffargv, diffargsmax * sizeof (char *));
  601.       if (! diffargv)
  602.     fatal ("out of memory");
  603.     }
  604.   diffargv[diffargs++] = a;
  605. }
  606.  
  607. static void
  608. execdiff (differences_only, option, file1, file2)
  609.      int differences_only;
  610.      char *option, *file1, *file2;
  611. {
  612.   if (differences_only)
  613.     diffarg ("--suppress-common-lines");
  614.   diffarg (option);
  615.   diffarg ("--");
  616.   diffarg (file1);
  617.   diffarg (file2);
  618.   diffarg (0);
  619.  
  620.   execvp (diffbin, diffargv);
  621.   write (fileno (stderr), diffbin, strlen (diffbin));
  622.   write (fileno (stderr), ": not found\n", 12);
  623.   _exit (2);
  624. }
  625.  
  626.  
  627.  
  628.  
  629. /* Signal handling */
  630.  
  631. static int volatile ignore_signals;
  632.  
  633. static void
  634. catchsig (s)
  635.      int s;
  636. {
  637.   signal (s, catchsig);
  638.   if (! ignore_signals)
  639.     {
  640.       cleanup ();
  641.       _exit (2);
  642.     }
  643. }
  644.  
  645. static void
  646. trapsigs ()
  647. {
  648.   static int const sigs[] = {
  649. #   ifdef SIGHUP
  650.       SIGHUP,
  651. #   endif
  652. #   ifdef SIGQUIT
  653.       SIGQUIT,
  654. #   endif
  655. #   ifdef SIGTERM
  656.       SIGTERM,
  657. #   endif
  658. #   ifdef SIGXCPU
  659.       SIGXCPU,
  660. #   endif
  661. #   ifdef SIGXFSZ
  662.       SIGXFSZ,
  663. #   endif
  664.       SIGINT,
  665.       SIGPIPE
  666.   };
  667.   int const *p;
  668.  
  669.   for (p = sigs;  p < sigs + sizeof (sigs) / sizeof (*sigs);  p++)
  670.     if (signal (*p, SIG_IGN) != SIG_IGN  &&  signal (*p, catchsig) != SIG_IGN)
  671.       fatal ("signal error");
  672. }
  673.  
  674.  
  675.  
  676. static void
  677. give_help ()
  678. {
  679.   fprintf (stderr,"l:\tuse the left version\n");
  680.   fprintf (stderr,"r:\tuse the right version\n");
  681.   fprintf (stderr,"e l:\tedit then use the left version\n");
  682.   fprintf (stderr,"e r:\tedit then use the right version\n");
  683.   fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
  684.   fprintf (stderr,"e:\tedit a new version\n");
  685.   fprintf (stderr,"s:\tsilently include common lines\n");
  686.   fprintf (stderr,"v:\tverbosely include common lines\n");
  687.   fprintf (stderr,"q:\tquit\n");
  688. }
  689.  
  690. static int
  691. skip_white ()
  692. {
  693.   int c;
  694.   while (isspace (c = getchar ()) && c != '\n')
  695.     ;
  696.   if (ferror (stdin))
  697.     perror_fatal ("input error");
  698.   return c;
  699. }
  700.  
  701. static void
  702. flush_line ()
  703. {
  704.   int c;
  705.   while ((c = getchar ()) != '\n' && c != EOF)
  706.     ;
  707.   if (ferror (stdin))
  708.     perror_fatal ("input error");
  709. }
  710.  
  711.  
  712. /* interpret an edit command */
  713. static int
  714. edit (left, lenl, right, lenr, outfile)
  715.      struct line_filter *left;
  716.      int lenl;
  717.      struct line_filter *right;
  718.      int lenr;
  719.      FILE *outfile;
  720. {
  721.   for (;;)
  722.     {
  723.       int cmd0, cmd1;
  724.       int gotcmd = 0;
  725.  
  726.       while (!gotcmd)
  727.     {
  728.       if (putchar ('%') != '%')
  729.         perror_fatal ("output error");
  730.       ck_fflush (stdout);
  731.  
  732.       cmd0 = skip_white ();
  733.       switch (cmd0)
  734.         {
  735.         case 'l': case 'r': case 's': case 'v': case 'q':
  736.           if (skip_white () != '\n')
  737.         {
  738.           give_help ();
  739.           flush_line ();
  740.           continue;
  741.         }
  742.           gotcmd = 1;
  743.           break;
  744.  
  745.         case 'e':
  746.           cmd1 = skip_white ();
  747.           switch (cmd1)
  748.         {
  749.         case 'l': case 'r': case 'b':
  750.           if (skip_white () != '\n')
  751.             {
  752.               give_help ();
  753.               flush_line ();
  754.               continue;
  755.             }
  756.           gotcmd = 1;
  757.           break;
  758.         case '\n':
  759.           gotcmd = 1;
  760.           break;
  761.         default:
  762.           give_help ();
  763.           flush_line ();
  764.           continue;
  765.         }
  766.           break;
  767.         case EOF:
  768.           if (feof (stdin))
  769.         {
  770.           gotcmd = 1;
  771.           cmd0 = 'q';
  772.           break;
  773.         }
  774.           /* falls through */
  775.         default:
  776.           give_help ();
  777.           flush_line ();
  778.           continue;
  779.         }
  780.     }
  781.  
  782.       switch (cmd0)
  783.     {
  784.     case 'l':
  785.       lf_copy (left, lenl, outfile);
  786.       lf_skip (right, lenr);
  787.       return 1;
  788.     case 'r':
  789.       lf_copy (right, lenr, outfile);
  790.       lf_skip (left, lenl);
  791.       return 1;
  792.     case 's':
  793.       suppress_common_flag = 1;
  794.       break;
  795.     case 'v':
  796.       suppress_common_flag = 0;
  797.       break;
  798.     case 'q':
  799.       return 0;
  800.     case 'e':
  801.       if (! tmpname && ! (tmpname = private_tempnam (0, "sdiff", 1, 0)))
  802.         perror_fatal ("temporary file name");
  803.  
  804.       tmpmade = 1;
  805.  
  806.       {
  807.         FILE *tmp = ck_fopen (tmpname, "w+");
  808.  
  809.         if (cmd1 == 'l' || cmd1 == 'b')
  810.           lf_copy (left, lenl, tmp);
  811.         else
  812.           lf_skip (left, lenl);
  813.  
  814.         if (cmd1 == 'r' || cmd1 == 'b')
  815.           lf_copy (right, lenr, tmp);
  816.         else
  817.           lf_skip (right, lenr);
  818.  
  819.         ck_fflush (tmp);
  820.  
  821.         {
  822.           pid_t pid;
  823.           int wstatus;
  824.  
  825.           ignore_signals = 1;
  826.  
  827.           pid = vfork ();
  828.           if (pid == 0)
  829.         {
  830.           char *argv[3];
  831.           int i = 0;
  832.  
  833.           argv[i++] = edbin;
  834.           argv[i++] = tmpname;
  835.           argv[i++] = 0;
  836.  
  837.           execvp (edbin, (char **) argv);
  838.           write (fileno (stderr), edbin, strlen (edbin));
  839.           write (fileno (stderr), ": not found\n", 12);
  840.           _exit (1);
  841.         }
  842.  
  843.           if (pid < 0)
  844.         perror_fatal ("fork failed");
  845.  
  846.           while (waitpid (pid, &wstatus, 0) < 0)
  847.         if (errno != EINTR)
  848.           perror_fatal ("wait failed");
  849.  
  850.           ignore_signals = 0;
  851.  
  852.           if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
  853.         fatal ("Subsidiary editor failed");
  854.         }
  855.  
  856.         if (fseek (tmp, 0L, SEEK_SET) != 0)
  857.           perror_fatal ("fseek");
  858.         {
  859.           /* SDIFF_BUFSIZE is too big for a local var
  860.          in some compilers, so we allocate it dynamically.  */
  861.           char *buf = (char *) xmalloc (SDIFF_BUFSIZE);
  862.           size_t size;
  863.  
  864.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  865.         ck_fwrite (buf, size, outfile);
  866.           ck_fclose (tmp);
  867.  
  868.           free (buf);
  869.         }
  870.         return 1;
  871.       }
  872.     default:
  873.       give_help ();
  874.       break;
  875.     }
  876.     }
  877. }
  878.  
  879.  
  880.  
  881. /* Alternately reveal bursts of diff output and handle user editing comands.  */
  882. static int
  883. interact (diff, left, right, outfile)
  884.      struct line_filter *diff;
  885.      struct line_filter *left;
  886.      struct line_filter *right;
  887.      FILE *outfile;
  888. {
  889.   for (;;)
  890.     {
  891.       char diff_help[256];
  892.       int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
  893.  
  894.       if (snarfed <= 0)
  895.     return snarfed;
  896.  
  897.       switch (diff_help[0])
  898.     {
  899.     case ' ':
  900.       puts (diff_help + 1);
  901.       break;
  902.     case 'i':
  903.       {
  904.         int lenl = atoi (diff_help + 1), lenr, lenmax;
  905.         char *p = index (diff_help, ',');
  906.  
  907.         if (!p)
  908.           fatal (diff_help);
  909.         lenr = atoi (p + 1);
  910.         lenmax = max (lenl, lenr);
  911.  
  912.         if (suppress_common_flag)
  913.           lf_skip (diff, lenmax);
  914.         else
  915.           lf_copy (diff, lenmax, stdout);
  916.  
  917.         lf_copy (left, lenl, outfile);
  918.         lf_skip (right, lenr);
  919.         break;
  920.       }
  921.     case 'c':
  922.       {
  923.         int lenl = atoi (diff_help + 1), lenr;
  924.         char *p = index (diff_help, ',');
  925.  
  926.         if (!p)
  927.           fatal (diff_help);
  928.         lenr = atoi (p + 1);
  929.         lf_copy (diff, max (lenl, lenr), stdout);
  930.         if (! edit (left, lenl, right, lenr, outfile))
  931.           return 0;
  932.         break;
  933.       }
  934.     default:
  935.       fatal (diff_help);
  936.       break;
  937.     }
  938.     }
  939. }
  940.  
  941.  
  942.  
  943. /* temporary lossage: this is torn from gnu libc */
  944. /* Return nonzero if DIR is an existent directory.  */
  945. static int
  946. diraccess (dir)
  947.      const char *dir;
  948. {
  949.   struct stat buf;
  950.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  951. }
  952.  
  953. /* Return nonzero if FILE exists.  */
  954. static int
  955. exists (file)
  956.      const char *file;
  957. {
  958.   struct stat buf;
  959.   return stat (file, &buf) == 0;
  960. }
  961.  
  962. /* These are the characters used in temporary filenames.  */
  963. static const char letters[] =
  964.   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  965.  
  966. /* Generate a temporary filename.
  967.    If DIR_SEARCH is nonzero, DIR and PFX are used as
  968.    described for tempnam.  If not, a temporary filename
  969.    in P_tmpdir with no special prefix is generated.  If LENPTR
  970.    is not NULL, *LENPTR is set the to length (including the
  971.    terminating '\0') of the resultant filename, which is returned.
  972.    This goes through a cyclic pattern of all possible filenames
  973.    consisting of five decimal digits of the current pid and three
  974.    of the characters in `letters'.  Data for tempnam and tmpnam
  975.    is kept separate, but when tempnam is using P_tmpdir and no
  976.    prefix (i.e, it is identical to tmpnam), the same data is used.
  977.    Each potential filename is tested for an already-existing file of
  978.    the same name, and no name of an existing file will be returned.
  979.    When the cycle reaches its end (12345ZZZ), NULL is returned.  */
  980.  
  981.  
  982. static char *
  983. private_tempnam (dir, pfx, dir_search, lenptr)
  984.      const char *dir;
  985.      const char *pfx;
  986.      int dir_search;
  987.      size_t *lenptr;
  988. {
  989.   static const char tmpdir[] = PVT_tmpdir;
  990.   static struct
  991.     {
  992.       char buf[3];
  993.       char *s;
  994.       size_t i;
  995.     } infos[2], *info;
  996.   static char buf[TMPNAMSIZE];
  997.   static pid_t oldpid = 0;
  998.   pid_t pid = getpid ();
  999.   register size_t len, plen;
  1000.  
  1001.   if (dir_search)
  1002.     {
  1003.       register const char *d = getenv ("TMPDIR");
  1004.       if (d != NULL && !diraccess (d))
  1005.     d = NULL;
  1006.       if (d == NULL && dir != NULL && diraccess (dir))
  1007.     d = dir;
  1008.       if (d == NULL && diraccess (tmpdir))
  1009.     d = tmpdir;
  1010.       if (d == NULL && diraccess ("/tmp"))
  1011.     d = "/tmp";
  1012.       if (d == NULL)
  1013.     {
  1014.       errno = ENOENT;
  1015.       return NULL;
  1016.     }
  1017.       dir = d;
  1018.     }
  1019.   else
  1020.     dir = tmpdir;
  1021.  
  1022.   if (pfx != NULL && *pfx != '\0')
  1023.     {
  1024.       plen = strlen (pfx);
  1025.       if (plen > 5)
  1026.     plen = 5;
  1027.     }
  1028.   else
  1029.     plen = 0;
  1030.  
  1031.   if (dir != tmpdir && !strcmp (dir, tmpdir))
  1032.     dir = tmpdir;
  1033.   info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0];
  1034.  
  1035.   if (pid != oldpid)
  1036.     {
  1037.       oldpid = pid;
  1038.       info->buf[0] = info->buf[1] = info->buf[2] = '0';
  1039.       info->s = &info->buf[0];
  1040.       info->i = 0;
  1041.     }
  1042.  
  1043.   len = strlen (dir) + 1 + plen + 8;
  1044.   for (;;)
  1045.     {
  1046.       *info->s = letters[info->i];
  1047.       sprintf (buf, "%s/%.*s%.5d%.3s", dir, (int) plen, pfx,
  1048.           pid % 100000, info->buf);
  1049.       if (!exists (buf))
  1050.     break;
  1051.       ++info->i;
  1052.       if (info->i > sizeof (letters) - 1)
  1053.     {
  1054.       info->i = 0;
  1055.       if (info->s == &info->buf[2])
  1056.         {
  1057.           errno = EEXIST;
  1058.           return NULL;
  1059.         }
  1060.       ++info->s;
  1061.     }
  1062.     }
  1063.  
  1064.   if (lenptr != NULL)
  1065.     *lenptr = len;
  1066.   return buf;
  1067. }
  1068.